[PoC] ASoC: SOF: Move audio support as sof-client (support for multiple cards) - #5815
Draft
ujfalusi wants to merge 100 commits into
Draft
[PoC] ASoC: SOF: Move audio support as sof-client (support for multiple cards)#5815ujfalusi wants to merge 100 commits into
ujfalusi wants to merge 100 commits into
Conversation
Protect IPC RX and FW state handler list unregister/dispatch paths with client_event_handler_mutex to match list registration and locking comments. Fixes: 5c19da3 ("ASoC: SOF: Use guard()/scoped_guard() for mutex locks where it makes sense") Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
We need to adjust the params based on the available and picked SSP blob in the similar way we do for DMIC. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
…election The dai copier configuration for playback and capture needs to be separated because it is not correct to configure the dai copier as part of the input format selection for both direction. The input format is the dai format for capture, but it is not for playback, for playback the dai format is on the output side. Currently we configure and adjust the params based on the DAI supported formats when configuring the input side of the copier but right after the format has been adjusted we reset it for playback and loose this information. When using a nocodec passthrough topology (which is a bug) we have SSP blobs supporting 32bit only, copier supporting 16/24/32 bit then on playback the dai and copier will be incorrectly configured: host.copier.in: S16_LE host.copier.out: S16_LE dai.copier.in: S16_LE SSP.blob: S32_LE (we only have S32_LE blobs) dai.copier.out: S16_LE (the dai constraint is ignored) To handle such case the handling of capture and playback streams must be changed: The input format (no changes to previous implementation): for playback it is the pipeline_params for capture it is the adjusted fe_params The output format (no change for capture direction): for playback it is the adjusted fe_params for capture it is the fe_params with this change path format configuration will be correct: host.copier.in: S16_LE host.copier.out: S16_LE dai.copier.in: S16_LE SSP.blob: S32_LE (we only have S32_LE blobs) dai.copier.out: S32_LE Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Print the same information as we print in _sof_ipc4_prepare_copier_module() since the prepare will be not called on system resume, only the host_config will be executed and tracking the stream tag for the host is valuable information. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
…load sof_ipc3_bytes_ext_put() copies header.length bytes from user space into cdata->data, but the amount of payload later sent to the firmware is taken from the ABI header's own size field. Nothing checks that the two agree, so a user claiming a size larger than the data it actually provided makes the driver send the stale tail of the previous control value to the DSP. The same stale tail is returned to user space by a subsequent bytes_ext_get() that does not read back from the DSP. Reject the payload if the ABI size field exceeds the data available in the TLV block. header.length has already been verified to be at least sizeof(struct sof_abi_hdr), so the subtraction cannot underflow. Fixes: 67ec2a0 ("ASoC: SOF: Add bytes_ext control IPC ops for IPC3") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
For aggregate DAIs (num_cpus > 1) the pre_trigger/post_trigger callbacks send pipeline state IPCs per-DAI without considering that multiple DAIs may share the same pipeline. This causes premature state transitions where the pipeline goes RUNNING before all link DMAs have started, or individual DAIs send redundant IPCs for shared pipelines. Fix this by checking the HDA stream running state in post_trigger: - START/PAUSE_RELEASE: defer RUNNING IPC until all DAIs sharing the same pipeline have their link DMA streams running - STOP/SUSPEND/PAUSE_PUSH: use pipeline state dedup so the PAUSED IPC is sent once regardless of how many DAIs share the pipeline The running-state check naturally handles all aggregate topologies: shared pipelines, independent pipelines, and mixed cases without requiring per-pipeline counters. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
DSP-initiated IPC notifications and firmware state changes are delivered to clients through two dedicated subscription lists (ipc_rx_handler_list and fw_state_handler_list), each with its own entry type, allocation and register/unregister API, all guarded by client_event_handler_mutex. This does not scale. Adding a new event type requires yet another list, entry struct and register/unregister pair, duplicating the same bookkeeping. Collapse the per-event lists into a single struct sof_client_ops that a client registers with sof_client_register_ops() from its probe and drops with sof_client_unregister_ops() from its remove. Adding a new event type then only adds a callback field to the ops structure. Rename client_event_handler_mutex to client_ops_mutex and track the clients that registered ops on client_ops_list. The dispatchers walk that list and invoke the relevant callback when present, so only clients that opted in are visited. Clients receive every notification and filter the message types they handle themselves. Assisted-by: GitHub-Copilot:claude-opus-4.8 Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
In order for clients to be able to handle notification they are interested, the sof_client_ipc_rx_dispatcher() must be called from rx handler. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Rename snd_sof_dsp_only_d0i3_compatible_stream_active() to the shorter snd_sof_dsp_state_is_d0i3_compatible(), which reads more naturally at the call site. No functional change. Assisted-by: GitHub-Copilot:claude-opus-4.8 Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The DSP can only enter the low-power D0i3 substate when every active stream tolerates it. snd_sof_dsp_state_is_d0i3_compatible() decides this today by scanning the audio PCM streams directly, which does not work once audio moves into a removable sof-client, and cannot account for a non-audio client that has its own D0i3-relevant activity. Add a d0i3_vote callback to struct sof_client_ops. A client reports one of three states via enum sof_d0i3_vote: no activity, active and D0i3 compatible, or active and incompatible. sof_client_get_d0i3_vote() folds the votes of all clients that implement the callback: - any SOF_D0I3_INCOMPATIBLE -> SOF_D0I3_INCOMPATIBLE (veto) - else any SOF_D0I3_COMPATIBLE_ACTIVE -> SOF_D0I3_COMPATIBLE_ACTIVE - else -> SOF_D0I3_NO_ACTIVITY Clients without the callback do not participate. The fold is associative, so combining the core scan with the client votes yields the same result as a single scan over all streams. snd_sof_dsp_state_is_d0i3_compatible() now folds the client vote into its own scan. No client implements the callback yet, so the vote is SOF_D0I3_NO_ACTIVITY and the behaviour is unchanged. Assisted-by: GitHub-Copilot:claude-opus-4.8 Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The mailbox descriptor is a DSP-architecture concept rather than a private core detail, and it will be needed by sof-client code that must not include sof-priv.h. Move struct snd_sof_mailbox from sof-priv.h to include/sound/sof.h, alongside the other shared DSP-level definitions. Add enum snd_sof_mailbox_type to identify the individual DSP mailboxes (FW info, DSP, host, stream, debug), terminated with SOF_MAILBOX_COUNT for future use. No functional change. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Add a client accessor that returns the struct snd_sof_mailbox for a given DSP mailbox, selected by enum snd_sof_mailbox_type. This lets sof-client code obtain a mailbox descriptor (e.g. the FW info box) and its offset without reaching into struct snd_sof_dev directly. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Add sof_client_is_dspless() and sof_client_get_num_cores() accessor functions to the sof-client API. These will be needed by the audio client driver to query DSP state without direct access to snd_sof_dev. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Add a client accessor returning the IPC4 firmware data (struct sof_ipc4_fw_data) stored in snd_sof_dev::private. This lets sof-client IPC4 code obtain the firmware data without reaching into struct snd_sof_dev directly. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The kcontrols of a widget are parsed before the widget itself, so the control load handlers assigned scontrol->comp_id by pre-reading the global next_comp_id counter, relying on the fact that the very next widget allocation would consume that same id. This coupling is fragile and reaches into the DSP-global id counter from the control path. Instead, assign each kcontrol the comp_id of the widget it belongs to once the widget has been created, walking the widget's kcontrols the same way the unload path does. The resulting comp_id is identical, but it is now derived from the owning widget rather than guessed from the allocator. No functional change. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
next_comp_id is a DSP-global monotonic comp_id allocator that may be accessed concurrently once a single DSP backs multiple audio cards/clients loading topology in parallel. Convert it to atomic_t and allocate ids with atomic_fetch_inc() so the increment is race-free. Also drop the misleading "reset during S3" note: the counter is never reset in code (snd_sof_dev persists across S3), it is only re-initialised when the device is re-probed. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Add a client accessor that allocates a new unique component id from the DSP-global monotonic comp_id counter using atomic_fetch_inc(). Component ids belong to the DSP instance, not to an individual audio client, so a single allocator shared by all clients keeps the ids unique even when one DSP backs multiple audio cards. There is no matching free: ids are only handed out, never released. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The system suspend target is recorded on the SOF device when a system suspend starts, and the PCM trigger path needs it to decide whether a D0i3 compatible stream can be left running over S0iX. Add sof_client_is_suspend_target_s0ix() so that audio code can query this without dereferencing struct snd_sof_dev. A predicate is used rather than exposing the suspend target itself, since audio code is only interested in the S0iX case and enum sof_system_suspend_state is private to the SOF core. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The topology file name resolved by the SOF core is needed by audio code, which should not dereference struct snd_sof_dev to reach it. Add sof_client_get_topology_name() so that the name can be queried via the sof-client API. Note that a client owning its own machine description has its own topology name and should only fall back to this one when the machine does not provide it. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The topology path prefix is used to build the full path of the topology file to be requested. Audio code needs it both for the topology name of the SOF device and for the one coming from the client's own machine description. Add sof_client_get_topology_prefix() so that it can be queried via the sof-client API instead of dereferencing struct snd_sof_dev. The path is a property of the SOF device and it is shared by all clients, there is no need for a per client copy of it. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Function topology loading is skipped when the user requested a specific topology file via the sof_tplg_filename module parameter, as that file is expected to be loaded as it is. Add sof_client_is_function_topology_disabled() so that audio code can query this without dereferencing struct snd_sof_dev. Note that this is separate from the topology.c local module parameter of the same name, which stays where it is. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The machine description selected by the SOF core is needed by audio code to reach the get_function_tplg_files() callback, but it should not be obtained by dereferencing struct snd_sof_dev. Add sof_client_get_machine() to query it via the sof-client API. The returned machine is shared by all clients. A client which owns a machine description of its own must use that one and only fall back to this one when it has none, so the expectation is that clients resolve the two once and use the result afterwards. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The sdev level mclk_id_override/mclk_id_quirk pair is set either from NHLT data or from the snd-sof-intel-hda-generic mclk_id module parameter and it has to be consulted when an SSP link is configured. Add sof_client_get_ssp_mclk_id_quirk() so that the quirk can be queried via the sof-client API instead of dereferencing struct snd_sof_dev. The accessor combines the two fields into a single query: it returns true and fills the output parameter only when a quirk is present, so callers cannot use the value without checking its validity. A signed sentinel would not work here as struct snd_sof_dev is zero initialized and 0 is a valid mclk_id. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Replace direct snd_soc_component_get_drvdata() lookups used to fetch struct snd_sof_dev with snd_sof_component_get_sdev() across sound/soc/sof/. This is a preparatory, mechanical cleanup to keep SOF component accessor naming consistent ahead of follow-up ownership refactoring. No functional behavior change is intended. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Convert the PCM callback family in snd_sof_dsp_ops to pass struct snd_soc_component * instead of struct snd_sof_dev *. Update the PCM callback wrappers and callback implementations to retrieve sdev with snd_sof_component_get_sdev(component). This is a preparatory, mechanical API-churn change only. No functional behavior change is intended, and sdev->component assignment is intentionally kept intact for now. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Convert the compress callback family in snd_sof_dsp_ops to pass struct snd_soc_component * instead of struct snd_sof_dev *. Update the compress callback wrappers and callback implementations to retrieve sdev with snd_sof_component_get_sdev(component). This is a preparatory, mechanical API-churn change only. No functional behavior change is intended. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Many log messages in the topology, control, PCM and audio-core paths were printing via sdev->dev even though the context is clearly bound to a specific component (scomp), widget (swidget), route (sroute) or control (scontrol). Logging through sdev->dev in those paths forces all messages to appear under the platform device, making it impossible to distinguish which audio component or pipeline instance triggered a given message. Switch all such messages to use the device that belongs to the available context. Several internal helper functions that received struct snd_sof_dev * solely to reach sdev->dev have also been simplified to derive sdev locally or drop the parameter entirely. sdev->dev is retained where there is no component context yet (e.g. early IPC notification handlers before the widget lookup, platform- level firmware loading, and places where the allocation is tied to the lifetime of sdev rather than the component). Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Change sof_ipc4_set_pipeline_state() to take the component instead of snd_sof_dev and update the Intel HDA call sites accordingly. This keeps the pipeline state path component-scoped and allows the use of the component device for logging. The snd_sof_dev is still derived locally for the IPC transmission. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Drop the snd_sof_dev::component field and the leftover assignment in sof_pcm_probe(). All users were converted to explicit component arguments or helper-based lookups in previous preparatory changes, so this pointer is no longer needed. No functional behavior change is intended. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
compr_get_dai_frame_counter(), get_dai_frame_counter() and get_host_byte_counter() are the last stream callbacks still living in snd_sof_dsp_ops. They are only ever reached from the PCM and compress delay reporting paths, so the audio code has to keep dereferencing sdev->pdata->desc->ops to call them, which is exactly what the audio client must not do. Move the three callbacks to struct sof_audio_ops and give them the component based prototype used by the rest of that structure. The accessors in ops.h resolve the audio instance from the component like their neighbours, and all call sites already have the component at hand. sof_ipc4_pcm_setup() takes it from spcm->scomp. None of the HDA implementations used the snd_sof_dev argument, so drop it from hda_dsp_get_stream_llp(), hda_dsp_compr_get_stream_llp(), hda_dsp_get_stream_ldp() and from the hda_dsp_get_llp() helper they share. With this no audio path reaches into snd_sof_dsp_ops any more. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
sof_client_audio_suspend() and sof_client_audio_resume() implement the power management of a single audio instance, but they live in sof-client.c, which is the generic client infrastructure of the SOF core. Their only caller is the PM callback pair of the audio client. Fold them into sof_audio_client_suspend() and sof_audio_client_resume() and let the audio core provide sof_audio_instance_suspend() and sof_audio_instance_resume() for the pipeline handling, so that the instance internals are not accessed from outside of the audio code. The moved code inspected sdev->dsp_power_state and sdev->fw_state directly. Use sof_client_get_fw_state() for the latter and add sof_client_is_dsp_in_d0() for the former to keep the client on the client API instead of the core internals. No functional change intended. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The pre-built snd_soc_component_driver of an audio instance is assembled by sof_audio_client_init_pdata(), which runs in the SOF core when the audio auxiliary device is registered. The template only describes the PCM operations of the client that is about to be created, so the client itself is a better place to build it. Move the snd_sof_new_platform_drv() call to sof_audio_client_probe() and convert the function to take the sof_client_dev instead of the snd_sof_dev, as the clients have no access to the latter. The IPC type and the DSPless mode already have client accessors, add sof_client_get_machine_drv_name() for the machine driver name, which hides whether the machine is described by an ACPI or an OF descriptor. No functional change intended. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
sof_client_ipc4_find_swidget_by_id() lets the probes client resolve a module instance to its topology widget. It is implemented in the SOF core, which walks the audio instances and calls into the audio code to do the lookup, one of the last places where the core depends on audio. Turn it into a get_module_name() callback of struct sof_client_ops. The core only asks the clients which implement the callback, so the lookup no longer needs the audio symbols. Letting the probes client call into the audio client instead would tie the two modules together and would need the audio module to be pinned for as long as probes is loaded. The callback copies the widget name instead of returning a pointer to it. Before audio became a client, snd_sof_device_remove() unregistered all clients before unbinding the machine driver, so the topology could not be unloaded while the probes client was still around to dereference a widget. Audio clients are now bound and unbound on their own, so that ordering no longer holds and the name is copied while the core still holds the client ops mutex. The name is only used for a debugfs string, truncation is harmless. It is not bound by the topology name length as the ASoC core may prefix the widget name with the component name. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The client event callbacks are dispatched with client_ops_mutex held, which restricts them to non sleeping work. A callback which sends an IPC message and waits for the reply would deadlock: the IPC IRQ thread handles both the replies and the DSP initiated notifications and it takes the same mutex in sof_client_ipc_rx_dispatcher(). If a notification arrives while the mutex is held then the IRQ thread blocks on it and the reply the callback is waiting for can never be delivered. On Intel platforms the BUSY interrupt is left masked until the notification handler returns, so the DSP cannot raise the next message either. None of the current callbacks sends IPC, but the firmware boot needs to notify the audio clients to let them restore their pipelines, which is IPC heavy. Protect the list with SRCU instead. The mutex is kept to serialize the writers while the dispatchers use the read side critical section, which is allowed to sleep. sof_client_unregister_ops() waits for the grace period to pass before it drops the ops pointer, a client module cannot be removed while one of its callbacks is still running. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
sof_restore_pipelines() walks the audio instances of the SOF device from the firmware boot path, which is one of the last places where the core reaches into the audio stack. Turn it into a fw_booted client callback: the core tells every client that the firmware has been (re)booted and that nothing of the state the client had in the DSP survived it, the audio client sets up the static pipelines of its own instance. Booting the DSP is aborted if a client fails to rebuild its state, as it was before. The set of clients which have a component is the set of the registered audio instances, sof_pcm_probe() sets the component when it registers the instance and clears it when the registration fails or the instance is unregistered, so the same instances are restored as before. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
snd_sof_stream_suspend_ignored() walks the PCMs of every audio instance of the SOF device to decide whether the DSP can be powered down when the system suspends to S0IX. The core has no business knowing what a stream is, and the audio client is not the only one which can leave something running in the DSP on purpose. Replace it with a keep_dsp_in_d0 client callback and fold the answers in sof_client_keep_dsp_in_d0(): any client which needs what it has in the DSP to survive keeps it in D0. The audio client answers for the streams of its own instance which ignored the suspend trigger. The wording is kept generic on purpose, the criteria listed in snd_sof_dsp_power_target() which are still to be implemented are not specific to audio streams either. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
snd_sof_sdev_get_tplg_ops() and snd_sof_sdev_get_pcm_ops() return the ops of the first audio instance found on the SOF device, which is not a meaningful answer once a device can have several instances. They have no users left in the tree, remove them. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Every platform pcm_pointer implementation starts with the same snd_sof_find_spcm_dai() lookup to get the snd_sof_pcm the substream belongs to. That lookup is the only reason why the AMD and the MediaTek platform drivers reference symbols owned by the audio code, which stands in the way of moving the audio support to a separate module. The core resolves the very same snd_sof_pcm a few lines after the callback is invoked, so pass it to the callback instead and drop the open coded lookup from the platform drivers. In sof_pcm_pointer() the lookup is moved ahead of the callback, which also makes the (theoretical) case of a missing snd_sof_pcm consistently reported as -EINVAL. The snd_sof_pcm_platform_pointer() wrapper in ops.h has no users, it is dropped rather than updated. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
…en() hda_dsp_compr_open() looks up the snd_sof_pcm for the compressed stream but it only uses it to bail out if it is not found. The core has already done the very same lookup before it calls the platform callback, so the check can never trigger. Drop the lookup, it serves no purpose. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The HDaudio pcm_open implementation repeats the snd_sof_find_spcm_dai() lookup which the core has already done just before it invokes the callback. Pass the resolved snd_sof_pcm to the callback and drop the open coded lookup, removing the last PCM lookup from the platform PCM callbacks. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
sof_ipc4_set_pipeline_state() sends the generic SET_PIPELINE_STATE message to the firmware. It is not tied to PCM handling in any way, it only happens to live in ipc4-pcm.c, which makes it one of the few symbols the HDaudio DAI code needs from the audio side. Move it next to sof_ipc4_pipeline_state_str() in ipc4.c and take the sof_client_dev directly instead of deriving it from the ASoC component, which is not needed for a plain IPC message. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
snd_sof_dsp_state_is_d0i3_compatible() walked the PCMs of every audio instance from the core and only then folded in the votes of the other clients, which made the core depend on the audio code. Move the PCM walk to the audio client as its d0i3_vote callback, the callback the core already has for exactly this purpose, and leave snd_sof_dsp_state_is_d0i3_compatible() as the fold of the client votes. The IPC4 side will need to participate in the same vote soon, this gives it a place to plug into. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The platform interrupt handler has no way to tell which audio instance owns the DMA stream which just completed a period, it only has the substream. Add a period_elapsed client callback and let the clients themselves decide whether the substream is theirs. The first client claiming the substream ends the search, the same way as the get_module_name callback works. The callback is special among the client ops: it is invoked from the interrupt handler, which may hold its own locks, so it must not sleep. The audio client only takes the ownership if the substream is bound to its own ASoC component, matching on the dai_link id alone is not enough as the ids are only unique within a card. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The audio instance of an ASoC component was looked up by walking the audio instance list of the SOF device, which the platform drivers had to follow as well to get to the topology ops. There is exactly one audio instance per audio client and the component already carries the client device as its drvdata, so save the instance in the client platform data and turn the component level getters into pointer dereferences. The getters are inline now, which removes the last symbol reference from the platform drivers to the audio code. The instance list itself is kept for the cross instance aggregations. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The last user of the instance list was the HDA D0i3 streaming check, which walked the PCMs of every audio instance to tell if all the active streams are D0i3 compatible and at least one of them is playback. The D0i3 vote of the clients already answers the first half, extend it with a playback flavour of the compatible vote to answer the second one as well and fold the votes by their precedence. With that the instance list, its lock and the iteration macro have no users left, an audio instance is only reachable via its own component. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The 12 objects implementing the ASoC component (PCM, topology, control and their IPC3/IPC4 backends) no longer reference anything provided by the platform drivers, so they can finally leave snd-sof.ko and join the audio client in snd-sof-audio.ko. Export the handful of core facilities they still need: the widget, position and topology tracepoints, sof_ipc4_find_module_by_uuid(), sof_ipc4_update_cpc_from_manifest() and sof_ipc4_pipeline_state_str(). snd-sof-audio.ko now depends on snd-sof.ko and the platform drivers only depend on snd-sof.ko, so there is no dependency cycle. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The tracepoints of the SOF core are declared in a single trace header, include/trace/events/sof.h. CREATE_TRACE_POINTS can only be defined in one translation unit per trace header, and that unit defines every event of the header, so the header has exactly one owning module: core.c in snd-sof.ko. Four of the six events are only used by the audio client module, which made it necessary to export them with EXPORT_TRACEPOINT_SYMBOL_GPL() to be able to use them from there. Move sof_widget_setup, sof_widget_free, sof_ipc3_period_elapsed_position and sof_pcm_pointer_position to a new header, include/trace/events/sof_audio.h, and instantiate it in the audio client. Each event is now defined by the module which uses it and none of them needs to be exported. The new header keeps TRACE_SYSTEM as sof and only overrides TRACE_INCLUDE_FILE, so the events stay under the same /sys/kernel/tracing/events/sof/ directory as before. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
With the audio objects living in snd-sof-audio.ko these symbols are only used within that module, no need to place them into the kernel's symbol table. The remaining exports have users outside of the module: the machine drivers call the DAI parameter helpers and snd-sof-nocodec.ko uses sof_pcm_dai_link_fixup(). Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
ujfalusi
force-pushed
the
peter/topic/multi_card_poc01
branch
from
August 7, 2026 08:12
4d87433 to
22fc861
Compare
When soc_check_tplg_fes() overrides BE DAI link CPU component names, only set cpu->name when the DAI is actually registered by the matching component. This prevents cross-component DAI binding issues when multiple components provide identically-named DAIs (e.g., SDW DAIs in soundwire_intel vs SOF audio components). Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
When a function topology does not support a DAI link, continue to the next one instead of failing the entire topology load. This allows partial topology loading where not all DAI links have matching function topologies. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
…links Before loading a user-specified feature topology, verify that the card has a matching BE DAI link. Feature topologies extend specific codec function types (amp, jack, mic) and reference widgets that only exist when the corresponding BE DAI links are present. Without this check, loading an amplifier feature topology on a card without SmartAmp DAI links would fail with -EINVAL due to missing route endpoints, killing the entire card probe. This is needed for multi-card configurations where cards are split by function type, but also provides a safety net in single-card mode when feature topologies don't match the hardware. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
When more than one card is created on top of the same audio DSP the sof_sdw machine driver must be able to name its card and to know which DAI types it owns, both coming from the machine parameters. Use mach_params.card_name in mc_probe() to name the card when it is provided and keep the "soundwire" name otherwise. Add the dai_type_mask field to snd_soc_acpi_mach_params, it is used by the SoundWire endpoint parsing in a follow-up patch. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Add dai_type_mask field filtering in asoc_sdw_count_sdw_endpoints() and asoc_sdw_parse_sdw_endpoints() to allow multi-card configurations to include only specific DAI types. When dai_type_mask is non-zero, only codec endpoints with matching dai_type bits are counted and parsed. Auxiliary devices for codecs without matching endpoints are also skipped. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Add Intel-specific multi-card audio client registration that creates separate sound cards per codec function type when the multi_card module parameter is set. The registration groups SoundWire endpoints by DAI type (jack, amp, mic) and creates a card per group using codec-specific names (e.g. cs42l43, cs35l56) with a generic function name fallback (jack, speaker, mic). DMIC and HDMI are registered as additional separate cards. Individual card registration failures are non-fatal to allow partial audio functionality when some components fail to probe. When multi_card is not set (default), falls back to the generic single-card sof_register_audio_client(). This is experimental and opt-in via: modprobe snd-sof-intel-hda-generic multi_card=1 Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Use mach_params.card_name when provided. Propagate mach_params.dmic_num to ctx->dmic_be_num. Machine data then controls DMIC BE exposure in this card. This enables per-audio-client card setup for split configurations. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Add split_hdmi mode to register separate analog and HDMI audio clients when iDisp is present. Keep the analog card on the selected machine configuration with iDisp masked out, and register HDMI as a dedicated generic HDA card using sof-hda-generic-idisp.tplg. Update unregister handling to support split fallback to the legacy single-client path when split registration is not used. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Collaborator
Author
|
Changes since v1:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The PR contains my PoC work in progress patches to rewrite SOF and detach the sound from the DSP core driver as 'independent' sof-client.
The DSP management, booting, IPC, PM, ID allocation still remain in the core sdev level, but audio is mostly detached from sdev into snd_sof_instance, which now owns the topology loading, widgets, pipelines, PCMs, component and sound card.
booting with this branch there must not be any functional change, everything should work as before wit the exception that we have a new
snd_sof_audiomodule.I have introduced two Intel specific module parameters to
snd_sof_intel_hda_generic:multi_card- applicable in case of function topologies and it will create sound cards per main codecs'split_hdmi` - this will separate only the HDMI card from the main card into it's own.
-- multi_card --
-- split_hdmi --
In either case, you can unbind any card runtime w/o affecting the operation of the other cards, like in
multi_card, one can remove the sof-hdmi card by:The
sof-hdmicard will always use the skl_hda machine driver with sof-hda-generic-idisp.tplg, so it's PCM list will be constant.The
split_hdmiworks with IPC3 hda and sndw machines as well.The topology name is moved from fw_profile of debugfs to audio.X/topology_name.
The branch should not case regression to @thesofproject/amd, @thesofproject/nxp or @thesofproject/mediatek, it works with nocodec and I2S machine drivers as well.
There are still layering violation around sdev accesses, some code still relies on sdev while it should not, NHLT blob is iffy a bit, but not much worst than what we have, but not ideal.
Concurrent card usage works fine as well, UIs will be broken for the
multi_cardbut likely going to work withsplit_hdmi, only that we will miss the HDMI from profile (likely) as we keep the original card's name for the card from where the HDMI is removed.